package com.dc.datacollect.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.dc.datacollect.components.cache.Cache;
import com.dc.datacollect.components.upload.FileResponseServerController;
import com.dc.datacollect.constant.ExportCollectConstant;
import com.dc.datacollect.constant.type.EndedCollectType;
import com.dc.datacollect.constant.type.UserSessionActivityKey;
import com.dc.datacollect.dto.ExpeditingUserDTO;
import com.dc.datacollect.dto.GroupUserDTO;
import com.dc.datacollect.dto.SysUserDTO;
import com.dc.datacollect.dto.input.CreateCollectInput;
import com.dc.datacollect.dto.input.SubmitCollectInput;
import com.dc.datacollect.dto.output.*;
import com.dc.datacollect.entity.*;
import com.dc.datacollect.helpers.EmailHelper;
import com.dc.datacollect.scheduled.CollectScheduled;
import com.dc.datacollect.service.*;
import com.dc.datacollect.mapper.CollectMapper;
import com.dc.datacollect.helpers.BeanProvide;
import com.dc.datacollect.utils.ReflectionUtils;
import com.dc.datacollect.helpers.SessionProvide;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.stream.Collectors;

/**
* @author zhuangjie
* @description 针对表【collect(收集表)】的数据库操作Service实现
* @createDate 2023-06-21 16:07:26
*/
@Service
@Slf4j
public class CollectServiceImpl extends ServiceImpl<CollectMapper, Collect>
    implements CollectService{
    @Autowired
    private CollectGroupService collectGroupService;

    @Autowired
    private CollectInstanceService collectInstanceService;

    @Autowired
    private Cache cache;

    @Autowired
    private CollectorService collectorService;

    @Autowired
    private ScheduledThreadPoolExecutor scheduledThreadPoolExecutor;

    @Autowired
    private BeanProvide beanProvide;

    @Autowired
    private PartService partService;
    @Override
    @Transactional
    public Boolean createCollect(CreateCollectInput createCollectInput) throws Exception {
        // 判断用户是不是收集者
        SysUserDTO loginObject = SessionProvide.getLoginObject();
        // 判断当前用户类型
        if (! loginObject.isCollector()) {
            // 不是收集者
            throw new Exception("你不是收集者");
        }
        // 判断时间
        long endTime = createCollectInput.getEndTime().getTime();
        long currentTime = System.currentTimeMillis();

        if ( endTime < currentTime) {
            throw new IllegalArgumentException("传入的参数不满足创建收集表");
        }
        Collect newCollect = new Collect();
        ReflectionUtils.copyProperties(createCollectInput,newCollect);
        newCollect.setCollectorId(loginObject.getId());
        // 查出收集者名下是否已经有同名的收集表
        LambdaQueryWrapper<Collect> collectQueryWrapper = new LambdaQueryWrapper<>();
        collectQueryWrapper.eq(Collect::getCollectorId,loginObject.getId())
                .eq(Collect::getTitle,createCollectInput.getTitle());
        Collect collect = this.getOne(collectQueryWrapper);
        if ( collect != null ) {
            throw new Exception("已存在同名收集表");
        }
        this.save(newCollect);
        collect = this.getOne(collectQueryWrapper);
        Long collectId = collect.getId();
        // 建立收集表与收集组的关联关系
        List<Long> groupItemIds = createCollectInput.getGroupIds();
        if (groupItemIds != null && groupItemIds.size() > 0) {
            List<CollectGroup> collectGroupList = groupItemIds.stream().map(groupItemId -> {
                CollectGroup collectGroup = new CollectGroup();
                collectGroup.setCollectId(collectId);
                collectGroup.setGroupId(groupItemId);
                return collectGroup;
            }).collect(Collectors.toList());
            collectGroupService.saveBatch(collectGroupList);
        }
        // 查询出关联的组中的用户-通知
        List<GroupUserDTO> groupUserList = this.baseMapper.selectUserByGroupIds(groupItemIds);
        Map<Long,List<GroupUserDTO>> groupIdAndGroupUserMap = new HashMap<>();
        groupUserList.stream().forEach(groupUser->{
            List<GroupUserDTO> aGroupUserList = groupIdAndGroupUserMap.get(groupUser.getGroupId());
            if (aGroupUserList == null) {
                groupIdAndGroupUserMap.put(groupUser.getGroupId(),Arrays.asList(groupUser));
            }else {
                aGroupUserList.add(groupUser);
            }
        });
        Set<Long> groupIds = groupIdAndGroupUserMap.keySet();
        for (Long groupId : groupIds) {
            List<GroupUserDTO> aGroupUserList = groupIdAndGroupUserMap.get(groupId);
            if (aGroupUserList == null || aGroupUserList.isEmpty()) {
                continue;
            }
            List<String> aGroupEmailList = aGroupUserList.stream().map(aGroupUser -> aGroupUser.getEmail()).collect(Collectors.toList());
            String groupName = aGroupUserList.get(0).getGroupName();
            Long dayNumber = (collect.getEndTime().getTime() - System.currentTimeMillis()) / 1000 / 60 / 60 / 24;
            new EmailHelper.Builder("收集服务器-新收集活动通知","你加入的组< "+groupName+" > 发布了新的收集活动「 "+collect.getTitle()+" 」, 收集活动说明信息：”"+collect.getDescribe()+"“，收集活动"+dayNumber+"天后结束！")
                    .build().asyncSend(aGroupEmailList);
        }
        // 添加定时器
        CollectScheduled collectScheduled = beanProvide.inject(CollectScheduled.class);
        collectScheduled.saveTimer(Arrays.asList(collect));
        return true;
    }

    @Override
    public Integer selectWaitSubmitCount(Long userId) {
        // 查询当前用户
        List<Collect> unCommittedAndNotExpiredCollect = this.baseMapper.selectUnCommittedAndNotExpired(userId);
        return unCommittedAndNotExpiredCollect.size();
    }

    @Override
    public List<Collect> getWaitSubmitAndNotExpire(Long userId) {
        return this.baseMapper.selectUnCommittedAndNotExpired(userId);
    }

    @Override
    @Transactional
    public Boolean submitCollect(SubmitCollectInput submitCollectInput) throws Exception {
        // 查询出收集活动
        Collect collect = this.getById(submitCollectInput.getCollectId());
        Long currentTime = System.currentTimeMillis();

        if (collect == null) {
            throw new Exception("收集活动不存在！");
        }
        if (collect.getEndTime().getTime()< currentTime ) {
            throw new Exception("收集活动已经结束，无法再提交！");
        } else if (collect.getStartTime().getTime() > currentTime ) {
            throw new Exception("你提交的收集活动，还没有开始！");
        }
        String fileUrl = SessionProvide.get(UserSessionActivityKey.COLLECT_FILE_KEY + submitCollectInput.getCollectId(), String.class);
        if (fileUrl == null ) {
            throw new Exception("要上传的文件不存在，请重新上传，再提交");
        }
        // 看是否提交过，如果之前删除原来的
        LambdaQueryWrapper<CollectInstance> collectInstanceQueryWrapper = new LambdaQueryWrapper<>();
        collectInstanceQueryWrapper.eq(CollectInstance::getUserId,SessionProvide.getLoginObject().getId())
                .eq(CollectInstance::getCollectId,collect.getId());
        CollectInstance userSubmitCollectInstance = collectInstanceService.getOne(collectInstanceQueryWrapper);
        if (userSubmitCollectInstance != null) {
            // 清理该记录
            collectInstanceService.removeById(userSubmitCollectInstance.getId());
        }
        // 查看collect下是否存在同名文件
        if (submitCollectInput.getCustomFileName() == null || submitCollectInput.getCustomFileName().isEmpty()) {
            // 未对文件重命名，将使用文件的名称
            String updateFileName = SessionProvide.get(UserSessionActivityKey.COLLECT_FILENAME_KEY + submitCollectInput.getCollectId(), String.class);
            submitCollectInput.setCustomFileName(updateFileName);
        }

        LambdaQueryWrapper<CollectInstance> findNameCollectInstanceQueryWrapper = new LambdaQueryWrapper<>();
        findNameCollectInstanceQueryWrapper.eq(CollectInstance::getCollectId,collect.getId())
                .eq(CollectInstance::getFileName,submitCollectInput.getCustomFileName());
        if (collectInstanceService.getOne(findNameCollectInstanceQueryWrapper) != null) {
            throw new Exception("你提交的名称此活动已有其人占用！");
        }

        // 构建CollectInstance对象，提交
        CollectInstance collectInstance = new CollectInstance();
        collectInstance.setCollectId(submitCollectInput.getCollectId());
        collectInstance.setFileName(submitCollectInput.getCustomFileName());
        collectInstance.setFile(fileUrl);
        collectInstance.setUserId(SessionProvide.getLoginObject().getId());
        return collectInstanceService.save(collectInstance);
    }

    @Override
    public List<InProgressCollectOutput> getInProgressCollect(Long userId) {
        // 查询正在进行中的收集活动（已提交、未提交）
        List<Collect> inProgresCollectList = this.baseMapper.selectInProgress(userId);
        // 正在进行中的收集活动 ids
        List<Long> inProgresCollectIds = inProgresCollectList.stream().map(inProgresCollect -> inProgresCollect.getId()).collect(Collectors.toList());
        if (inProgresCollectIds.isEmpty()) {
            // 没有进行中的Collect 返回空数据
            return new ArrayList<>();
        }
        // 查询用户参与的活动 & 进行中的收集活动
        LambdaQueryWrapper<CollectInstance> collectInstanceQueryWrapper = new LambdaQueryWrapper<>();
        collectInstanceQueryWrapper.eq(CollectInstance::getUserId,userId)
                        .in(CollectInstance::getCollectId,inProgresCollectIds);
        List<CollectInstance> userParticipatedCollectList = collectInstanceService.list(collectInstanceQueryWrapper);
        List<Long> userParticipatedCollectIds = userParticipatedCollectList.stream().map(upc -> upc.getCollectId()).collect(Collectors.toList());
        // 给进行中的活动添加（已经提交、未提交）标识
        List<InProgressCollectOutput> inProgressCollectOutputList = inProgresCollectList.stream().map(inProgresCollect -> {
            InProgressCollectOutput inProgressCollectOutput = new InProgressCollectOutput();
            ReflectionUtils.copyProperties(inProgresCollect, inProgressCollectOutput);
            // 如果已经collectId在用户 进行中 & 已经提交的 的collectId那就设为已经提交过了
            inProgressCollectOutput.setSubmitted(userParticipatedCollectIds.contains(inProgresCollect.getId()));
            return inProgressCollectOutput;
        }).collect(Collectors.toList());
        return inProgressCollectOutputList;
    }

    @Override
    public Page<EndedCollectOutput> getHistoryCollectList(Page<Collect> page,
                                                          String keyword,
                                                          Short type) throws Exception {
        Long userId = SessionProvide.getLoginObject().getId();
        Page<Collect> collectPage = null;
        Boolean isCompleted = EndedCollectType.ENDED_COMPLETE.equals(type);
        if (isCompleted) {
            // 查询的是结束-用户已经参加的
            collectPage = this.baseMapper.selectEndedCompleteCollectList(page, keyword, userId);
        }else {
            // 查询的是结束-用户未参加的
            collectPage = this.baseMapper.selectEndedUnCompleteCollectList(page, keyword, userId);
        }
        if (collectPage == null ) {
            throw new Exception("查询失败！");
        }
        Page<EndedCollectOutput> endedCollectOutputPage = new Page<>();
        ReflectionUtils.copyProperties(collectPage,endedCollectOutputPage);
        List<Collect> records = collectPage.getRecords();
        List<EndedCollectOutput> newRecords = records.stream().map(endedCollect -> {
            EndedCollectOutput endedCollectOutput = new EndedCollectOutput();
            ReflectionUtils.copyProperties(endedCollect, endedCollectOutput);
            endedCollectOutput.setIsCompleted(isCompleted);
            return endedCollectOutput;
        }).collect(Collectors.toList());
        endedCollectOutputPage.setRecords(newRecords);
        return endedCollectOutputPage;
    }

    @Override
    public Page<ConsoleCollectOutput> consoleCollectPageData(Page<ConsoleCollectOutput> page, String keyword) {
        Long collectorId = SessionProvide.getLoginObject().getId();
        Page<ConsoleCollectOutput> consoleCollectOutputPage = this.baseMapper.selectConsolePage(page, keyword, collectorId);
        return consoleCollectOutputPage;
    }

    @Override
    public String exportCollect(Long collectId) throws Exception {
        Long collectorId = SessionProvide.getLoginObject().getId();
        // 查看该收集活动是不是当前收集者的
        LambdaQueryWrapper<Collect> collectQueryWrapper = new LambdaQueryWrapper<>();
        collectQueryWrapper.eq(Collect::getId,collectId)
                        .eq(Collect::getCollectorId,collectorId);
        Collect collect = this.getById(collectId);
        if (collect == null ) {
            throw new Exception("该收集活动不存在");
        }
        if (! collect.getCollectorId().equals(collectorId)) {
            throw new Exception("你不是该收集活动的收集者！");
        }
        String exportActiveKey = UserSessionActivityKey.GET_EXPORT_COLLECT_EVENT(collectId);
        scheduledThreadPoolExecutor.schedule(()->{
            // 如果超过2分钟还没有导出完成，就清理掉标志，相同导出请求可再次请求导出
            log.info("--准备清理掉导出标志--");
            cache.remove(exportActiveKey);
        }, ExportCollectConstant.MAX_EXPORT_INTERVAL,ExportCollectConstant.TIME_UNIT );
        synchronized (this) {
            // 判断是否已经有相同的导出活动
            if ( cache.get(exportActiveKey) != null ) {
                throw new Exception("已经存在相同的导出活动，请不要重复操作。");
            }
            cache.set(UserSessionActivityKey.EXPORT_COLLECT_EVENT+collectId,true);
        }
        String compressDownloadUrl = collectInstanceService.repayableExportCollectHandle(collectId);
        log.info("请求返回：{}",compressDownloadUrl);
        return compressDownloadUrl;
    }

    @Override
    @Transactional
    public Boolean removeCollectById(Long collectId) throws Exception {
        // 删除参与者提交的数据
        LambdaQueryWrapper<CollectInstance> collectInstanceQueryWrapper = new LambdaQueryWrapper<>();
        collectInstanceQueryWrapper.eq(CollectInstance::getCollectId,collectId);
        collectInstanceService.remove(collectInstanceQueryWrapper);
        // 删除收集活动
        if (! this.removeById(collectId)) {
            throw new Exception("删除收集活动失败！");
        }
        // 将定时任务删除(如果有的话)
        CollectScheduled collectScheduled = beanProvide.inject(CollectScheduled.class);
        collectScheduled.removeTimer(collectId);
        return true;
    }

    @Override
    @Transactional
    public Boolean updateCollect(Collect collect) throws Exception {
        Long collectId = collect.getId();
        if (collectId == null) {
            throw new Exception("更新必须要带id");
        }
        // 更新关联的组
        LambdaQueryWrapper<CollectGroup> collectGroupQueryWrapper = new LambdaQueryWrapper<>();
        collectGroupQueryWrapper.eq(CollectGroup::getCollectId,collect.getId());
        // -- 全部删除关联
        collectGroupService.remove(collectGroupQueryWrapper);
        if (collect.getGroupIds() != null && ! collect.getGroupIds().isEmpty()) {
            // -- 如果有关联，添加提交的关联
            List<CollectGroup> collectGroupList = collect.getGroupIds().stream().map(groupId -> {
                CollectGroup collectGroup = new CollectGroup();
                collectGroup.setGroupId(groupId);
                collectGroup.setCollectId(collect.getId());
                return collectGroup;
            }).collect(Collectors.toList());
            collectGroupService.saveBatch(collectGroupList);
        }
        // TODO 这里用户的提交记录暂时选择不删除
        // 更新收集活动Collect信息
        Collect oldCollect = this.getById(collect.getId());
        BeanUtils.copyProperties(collect,oldCollect);
        boolean updateCollect = this.updateById(oldCollect);
        if (! updateCollect) {
            throw new Exception("更新收集活动失败！");
        }
        // 更新定时器
        CollectScheduled collectScheduled = beanProvide.inject(CollectScheduled.class);
        collectScheduled.saveTimer(Arrays.asList(oldCollect));
        return true;
    }

    @Override
    public Page<CollectUserOutput> selectCollectUserPage(Page<CollectUserOutput> collectUserOutputPage,Long collectId, String keyword, Long groupId, Short status) {
        Page<CollectUserOutput> collectUserOutputPageData = this.baseMapper.selectCollectUserPage(collectUserOutputPage,collectId,keyword,groupId,status);
        return collectUserOutputPageData;
    }

    @Override
    public List<CollectAssociatedGroupOutput> selectCollectGroupList(Long collectId) {
        List<CollectAssociatedGroupOutput> collectAssociatedGroupOutputs = this.baseMapper.selectCollectGroupList(collectId);
        return collectAssociatedGroupOutputs;
    }

    @Override
    public void expediting(Long collectId) throws Exception {
        // 判断collectId是否为当前收集者的
        LambdaQueryWrapper<Collect> collectQueryWrapper = new LambdaQueryWrapper<>();
        collectQueryWrapper.eq(Collect::getCollectorId,SessionProvide.getLoginObject().getId())
                .eq(Collect::getId,collectId);
        Collect collect = this.getOne(collectQueryWrapper);
        if (collect == null) {
            throw new Exception("活动不存在或你不是该收集活动的创建者！");
        }
        // 查看当前活动是否过期
        if (collect.isExpire()) {
            throw new Exception("活动已经结束，不可以进行催收！");
        }

        List<ExpeditingUserDTO> expeditingUserList = this.baseMapper.getCollectUnSubmitUser(collectId);
        // 过滤掉为空的expeditingUserList项
        expeditingUserList = expeditingUserList.stream().filter(expeditingUser -> expeditingUser != null).collect(Collectors.toList());

        if (expeditingUserList == null || expeditingUserList.isEmpty()) {
            throw new Exception("当前没有可催交的用户！");
        }
        String collectTitle = collect.getTitle();
        String finalSubmissionTime = new SimpleDateFormat("yyyy-MM-dd HH:mm").format(collect.getEndTime());
        Long finalSubmissionDays = (collect.getEndTime().getTime() - System.currentTimeMillis()) / 1000 / 60 / 60 / 24;
        String finalSubmissionDayStr = String.valueOf(finalSubmissionDays);
        if (finalSubmissionDays == 0) {
            finalSubmissionDayStr = "不到1";
        }
        Collector collector = collectorService.getById(collect.getCollectorId());
        List<String> noticeEmailList = expeditingUserList.stream().map(expeditingUser -> expeditingUser.getEmail()).collect(Collectors.toList());
        new EmailHelper.Builder("收集服务器-收集通知","您未提交 「 "+collectTitle+" 」,最后提交时间为"+finalSubmissionTime+", 距离结束，还有"+finalSubmissionDayStr+"天！")
                .callback((isOk)->{
                    String message = isOk?"通知成功":"通知失败";
                    new EmailHelper.Builder("收集服务器-"+message,message+"!")
                            .build().send(Arrays.asList(collector.getEmail()));
                })
                .build().asyncSend(noticeEmailList);
    }

}




